function PauseAndTalk(runFunc)
    local childs = g_Data.scene.layer:getChildren();
    
    local ck = os.clock();
    
    local function postFunc()
        g_Data.boss = false;
        ck = os.clock() - ck;
        g_Data.startCk = g_Data.startCk + ck;
        g_Data.isRunning = true;
        g_Data.scene.layer:setEnabled(true)
        ResumeScene();
        local childs2 = g_Data.scene.layer:getChildren();
        for i=1, #childs2 do
            local nodeA = childs2[i];
            if nodeA:getName() == "tmp" then
                RemoveCallback(nodeA);
            else
                nodeA:resume();
                if nodeA:getName() == "monster" then
                    nodeA:setVisible(true);     
                end
            end            
        end
    end

    g_Data.isRunning = false;
    g_Data.role:stopActionByTag(GAME_TAG.ACTION_MOVE_TAG);
    g_Data.isInMove = false;
    for i=1, #childs do
        local nodeA = childs[i];
        if nodeA:getName() == "monster" then
            nodeA:pause();
            nodeA:setVisible(false);
            
            local v = EXCEL.monster[nodeA.mid];
            local nodeMonster = LoadArmature(v.await, g_Data.scene.layer, "tmp");
            nodeMonster:setPosition(nodeA:getPosition());
            nodeMonster:setName("tmp")
        end
    end
    g_Data.scene.layer:setEnabled(false)
    runFunc(postFunc);
    
    return true;
end

local function GetTpAction(monster, node, speed, move, way, setOri)
    local action = nil;
    local loopAction = nil;
    for i=1,#way-1 do
        local srcX = way[i][1];
        if 1 == i then
            --srcX = g_Data.scene:getContentSize().width + node:getContentSize().width/2;
        end
        local srcY = way[i][2];
        if 1 == i and setOri then
            node:setPosition(cc.p(srcX, srcY))
        end
        local dstX = way[i+1][1];
        local dstY = way[i+1][2];
        if i == #way-1 and not (move.loop>0 and move.loop<=#way) then
            --dstX = -GetNodeSize(node).width/2;
        end
        local dist = GetDist(dstX, dstY, srcX, srcY);
        local a = nil;
        if way[i] == way[i+1] then
            a = cc.DelayTime:create(move.wait)
        else
            a = cc.MoveTo:create(dist/speed, cc.p(dstX, dstY));
        end
        if not action then
            local function func()
                if monster.tp==3 then
                    local function runFunc(postFunc)
                        local v = EXCEL.monster[monster.mid];
                        EnterBossTalk(g_Data.ui, v, postFunc);
                    end
                    PauseAndTalk(runFunc);
                end
            end
            
            action = cc.Sequence:create(a, cc.CallFunc:create(func));
        else
            if move.loop>0 and move.loop<=i and move.loop<=#way then
                if not loopAction then
                    function _resetPosition(node)
                        node:setPosition(cc.p(way[move.loop][1], way[move.loop][2]));
                    end
                    loopAction = cc.Sequence:create(cc.CallFunc:create(_resetPosition),a)
                else
                    loopAction = cc.Sequence:create(loopAction, a)
                end
            else
                action = cc.Sequence:create(action, a)
            end
        end
    end
    if loopAction then
        loopAction = cc.RepeatForever:create(loopAction)
    end
    return action, loopAction;
end

local function MoveMonster(monster, node, speed, moveId, func)
    local move = EXCEL.move[moveId];
    if move then
        node:stopActionByTag(GAME_TAG.ACTION_MOVE_TAG);
        local way = move.way;
        node:setPosition(cc.p(way[1][1],way[1][2]))
        local x,y = node:getPosition();
        local action, loopAction = GetTpAction(monster, node, speed,move,way, true);
        if loopAction then
            function _runLoopAction(node)
                local action, loopAction = GetTpAction(monster, node, speed,move,way);
                action:setTag(GAME_TAG.ACTION_MOVE_TAG);
                node:runAction(loopAction);
            end
            action = cc.Sequence:create(action, cc.CallFunc:create(_runLoopAction))
        elseif func then
            action = cc.Sequence:create(action, cc.CallFunc:create(func))
        end
        action:setTag(GAME_TAG.ACTION_MOVE_TAG);
        node:runAction(action)
    end
end

local function AddMonster(monster)
    local v = EXCEL.monster[monster.mid];
    if v then
        if monster.tp == 3 then
            g_Data.boss = true;
            g_Data.ui:getChildByName("bossBlood"):setVisible(true);
            g_Data.ui:getChildByName("bossBloodPng"):setVisible(true);
            if v and v.icon and v.icon~="" then
                g_Data.ui:getChildByName("monsterIcon"):setVisible(true);
                g_Data.ui:getChildByName("monsterIconPng"):setVisible(true);
                g_Data.ui:getChildByName("monsterIcon"):loadTexture(v.icon)
            end            
        end
        local nodeMonster = LoadArmature(v.await, g_Data.scene.layer, "monster");
        if nodeMonster then
            for k,v in pairs(monster) do
                nodeMonster[k] = v;
            end
            nodeMonster.chp = nodeMonster.hp;
            nodeMonster:setLocalZOrder(1);
            if monster == g_Data.firstMonster then 
                g_Data.firstMonster = nodeMonster;
                if g_Data.gate.id == EXCEL.const.newbieGate then
                    local function runFunc(postFunc)
                        local v = g_Data.gate.talk[1];
                        if v and #v>=2 then
                            EnterTalk(g_Data.ui, v, postFunc);
                        else
                            postFunc();
                        end
                    end

                    local a = cc.DelayTime:create(2)
                    local function func()
                        PauseAndTalk(runFunc);
                    end
                    a = cc.Sequence:create(a, cc.CallFunc:create(func))
                    g_Data.role:runAction(a);
                end
            end
            local s = GetNodeSize(nodeMonster);
            local ly = s.height/2;
            local hy = g_Data.scene.height- s.height/2;
            local y = math.random(ly, hy);
            local lx = s.width/2;
            local hx = g_Data.scene.width- s.width/2;
            local x = math.random(lx, hx);
            local x = g_Data.scene.width- math.random(s.width/2, s.width);
    
            MoveMonster(monster, nodeMonster,monster.speed,monster.move,RemoveCallback)
        end
    end
end

local function JumpToRole(node,speed, rx,ry)
    local x,y = node:getPosition();
    local x1,y1 = g_Data.role:getPosition();
    rx = rx or x1;
    ry = ry or y1;
    local s = GetNodeSize(g_Data.role);
    rx = s.width/2*1
    ry = - s.height/2*1
    local t = GetDist(x,y,rx,ry)/speed
    local action  = cc.MoveTo:create(t, cc.p(rx,ry))
    action = cc.Sequence:create(action, cc.CallFunc:create(RemoveCallback));
    node:runAction(action);
end

function StopGate(postFunc)
    g_Data.isRunning = false;
    g_Data.scene:unscheduleUpdate()
    local childs = g_Data.scene.layer:getChildren();
    for i=1, #childs do
        childs[i]:stopAllActions();
    end
    
    EnterTalk(g_Data.ui, g_Data.gate.talk[3], postFunc);
end

local function GateDefeat()
    g_Data.gameover = true;
    StopGate(EnterDefeat);
end

function GateVictory()
    g_Data.gameover = true;
    if g_Data.gate.id == EXCEL.const.newbieGate then
        local function postFunc()
            EnterGates();
            local function postFunc()
                g_Data.worldScene.layer:setEnabled(true);
            end
            g_Data.worldScene.layer:setEnabled(false);
            EnterTalk(g_Data.worldScene.layer, g_Data.gate.talk[4], postFunc);
        end
        StopGate(postFunc);
    else
        StopGate(EnterVictory);
    end
end

local function MonsterDie(node)
    node:setName("monsterDie");
    node:stopAllActions();
    
    local item = node.item;
    local k, v = next(item);
    if v and #v>0 then
        local x,y = node:getPosition();
        local a = node:getAnchorPoint();
        local item = EXCEL.item[v[1]];
        local itemPath = item.path;
        local parent = g_Data.scene.layer;
        if 2 == item.attr then
            parent = g_Data.role;
            local rx, ry = g_Data.role:getPosition();
            x = x - rx;
            y = y - ry;
        end
        local t = AddNode(itemPath, "item", parent);
        local p = t:getAnchorPoint();
        t:setPosition(cc.p(x,y))
        t:setTag(item.attr);
        if 2 == item.attr then
            JumpToRole(t, EXCEL.const["dropSpeed2"]);
            AddSitemData(item.attr, v[2]);
        else
            local rx = -t:getContentSize().height/2;
            local ry = y;
            local a = {};
            local s = t:getContentSize();
            for i=1,10 do
                local mx = math.random(s.width, g_Data.scene.width-s.width);
                local my = math.random(s.height, g_Data.scene.height-s.height)
                if i%2==1 then
                    if math.random(1,2)==1 then 
                        a[i] = {mx, s.height}
                    else
                        a[i] = {mx,g_Data.scene.height-s.height}
                    end
                else
                    if math.random(1,2)==1 then 
                        a[i] = {s.width, my}
                    else
                        a[i] = {g_Data.scene.width-s.width, my}
                    end
                end
            end
            local action = nil;
            for i=1,#a do
                local nextX = a[i][1];
                local nextY = a[i][2];
                local tmpAction  = cc.MoveTo:create(GetDist(x,y,nextX, nextY)/EXCEL.const["dropSpeed1"]
                , cc.p(nextX, nextY))
                x = nextX;
                y = nextY;
                if not action then
                    action = tmpAction;
                else
                    action = cc.Sequence:create(action, tmpAction)
                end
            end
            t:runAction(action);
            
            local action = cc.DelayTime:create(7)
            action = cc.Sequence:create(action, cc.Blink:create(3, 10), cc.CallFunc:create(RemoveCallback))
            
            t:runAction(action);
        end
    end
    
    if g_Data.role.chp>=0 and #g_Data.monsterQueue<=0 then
        local zeroMonster = true;
        local childs = g_Data.scene.layer:getChildren();
        for i=1,#childs do
            if childs[i]~=node and string.find(childs[i]:getName(), "monster") then
                zeroMonster = false;
                break;
            end
        end
        if zeroMonster then
            g_Data.attackAble = false;
        end
    end
    
    local x,y = node:getPosition();
    x = x + EXCEL.const["dieEffectMove"][1];
    y = y + EXCEL.const["dieEffectMove"][2];
    
    local v = EXCEL.monster[node.mid];
    local bossDie = v and v.die and type(v.die)=="table" and #v.die>=2;
    
    local function dieFunc(node1, args)
        node:setVisible(false);
        local function bossDieFunc()
            RemoveCallback(node1);
            RemoveCallback(node);
        end
        local p = LoadArmature(EXCEL.const["bossdie"], g_Data.scene.layer, nil, nil, bossDieFunc);
        p:setPosition(cc.p(x,y))
    end
    
    if not bossDie then
        RemoveCallback(node);
    else
        node:setVisible(false)
    end
    
    if v.die and #v.die>=2 then
        local p = LoadArmature(v.die, g_Data.scene.layer, nil, nil, RemoveCallback);
        p:setPosition(cc.p(x,y))
    end

    local p = LoadArmature(EXCEL.const["monsterDieAnimation"], g_Data.scene.layer, nil, nil
        , bossDie and dieFunc or RemoveCallback);
    p:setPosition(cc.p(x,y))
end

local function UpdatePlayerBlood()
    local percent = 100 * g_Data.role.chp / g_Data.role.hp;
    g_Data.scene.playerBlood:setPercent(percent)
    local msg = string.format("%s/%s", g_Data.role.chp, g_Data.role.hp);
    g_Data.scene.playerHp:setString(msg)
end

function ReleaseMonsterSkill(node)
    if g_Data.boss then
        return false;
    end
    
    local data = node;
    local com = data.com;
    local t = g_Data.mcds[node] or {}
    g_Data.mcds[node] = t;
    local now = os.clock();
    if data.com and type(data.com)=="table" and next(data.com)
        and (not t.es or (t.es and t.ee))
        and (not t.com or (now-t.com-(t.es and (t.ee-t.es) or 0))*1000>=com[6]) 
        --and (not t.com or (now-t.com)*1000>=com[6]) 
        then
        t.com = now;
        t.es = nil;
        t.ee = nil;

        local bulletNode = LoadArmature(com, g_Data.scene.layer, "mbullet");
        bulletNode.level = node.level;
        bulletNode.pa = node.pa;
        bulletNode.ma = node.ma;
        bulletNode:setLocalZOrder(3);
        g_Data.mbullets[bulletNode] = data;
        local x,y = node:getPosition();
        bulletNode:setPosition(cc.p(x+com[3],y+com[4]))
        local dstY = nil;
        if data.follow then
            local x2,y2 = g_Data.role:getPosition();
            if not IsFloatEqual(y,y2) then
                --(x-x2)/x=(y-y2)/(y-dstY)
                --y - dstY = (y-y2)*x/(x-x2);
                dstY = y - (y-y2)*x/(x-x2);
            end
        end
        MoveTo(bulletNode, com[5], 0, dstY, RemoveCallback)
    end
 
    local skill = EXCEL.skill[data.skill[1] or 0];
    if skill and (not t.skill or (now-t.skill)*1000>=skill.cd)
    then
        t.skill = now;
        
        local follow = (skill.follow);
        local parent = follow and g_Data.role or g_Data.scene.layer;
        local m = EXCEL.monster[data.mid];
        if m and next(m.attack) then
            local function PlayWait(node)
                PlayArmature(node, m.await)
            end
            
            PlayArmature(node, m.attack, PlayWait)
        end
        
        t.ee = nil;
        t.es = now;
        local function callFunc(node)
            t.ee = os.clock();
            RemoveCallback(node);
        end
        
        local follow = (skill.follow);
        local parent = follow and node or g_Data.scene.layer;
        
        local bulletNode = LoadArmature(skill.effect, parent, "mskill", nil, callFunc);
        bulletNode.level = node.level;
        bulletNode.pa = node.pa;
        bulletNode.ma = node.ma;
        bulletNode:setLocalZOrder(2);
        g_Data.mbullets[bulletNode] = data;
        
        local px,py = node:getPosition();
        local ps = node:getContentSize();
        local pa = node:getAnchorPoint();
        
        local xShift = skill.effect[3];
        local yShift = skill.effect[4];
        if follow then
            xShift = ps.width * pa.x + xShift;
            yShift = -ps.height * pa.y + yShift;
        else
            xShift = xShift;
            yShift = yShift;
        end
        bulletNode:setPosition(cc.p(xShift, yShift))
        local dstY = nil;
        local function callFunc(node)
            t.ee = os.clock();
            --RemoveCallback();
        end
        --MoveTo(bulletNode, data.skill[5], 0, dstY, callFunc)
    end
    
    return true;
end

function ReleaseSkill(skill)
    if not skill then
        return false;
    end
    
    local rx,ry = g_Data.role:getPosition();
    if not g_Data.skillAble then
        if skill~=g_Data.skNor then
            UtilDebugLog("not g_Data.skillAble")
        end
        return false;
    end
    
    if g_Data.boss then
        if skill~=g_Data.skNor then
            UtilDebugLog("g_Data.boss")
        end
        return false;
    end
    
    local effect = skill.effect;
    if skill==g_Data.skNor then
        effect = EXCEL.com[g_Player.comLevel].effect;
    end
    
    local now = os.clock();
    local skTm = g_Data.skcds[tostring(g_Data.role)..skill.id];
    if skTm and (now-skTm)*1000<skill.cd then
        return false;
    end
    
    if g_Data.curSk then
        return false;
    end
    
    g_Data.curSk = skill;
    g_Data.operable = skill.operable;
    if skill == g_Data.nextSk then
        g_Data.nextSk = nil;
    end
    
    g_Data.skcds[tostring(g_Data.role)..skill.id] = now;
    
    local role = g_Data.role;
    local x,y = role:getPosition();
    
    local speedRole = skill.attack[3] and skill.attack[3] or 0;
    local distRole = skill.attack[4] and skill.attack[4] or 0;
    
    local xShift = effect[3] and effect[3] or 0;
    local yShift = effect[4] and effect[4] or 0;
    local speedEffect = effect[5] and effect[5] or 0;
    local distEffect = effect[6] and effect[6] or 0;
    
    local speed = speedRole;
    local dist = distRole;

    if speed>0 then
        if dist > 0 then
            dist = x + dist;
        else
            dist = g_Data.scene:getContentSize().width;
        end
        if dist > g_Data.scene:getContentSize().width then
            dist = g_Data.scene:getContentSize().width;
        end
    end
    
    if skill.follow then
        speedEffect = 0;

        if speedRole>0 then
            speedEffect = speedRole;
            if distRole > 0 then
                distEffect = x + xShift + distRole;
            else
                distEffect = g_Data.scene:getContentSize().width;
            end
        end
    else
        if distEffect > 0 then
            distEffect = x + xShift + distEffect;
        else
            distEffect = g_Data.scene:getContentSize().width;
        end
    end

    g_Data.nextArmature = skill.attack;
    
    if speed>0 then
        MoveTo(g_Data.role, speed, dist)
    end

    local function SkillEnd(node,skill)
        RemoveCallback(node);
        g_Data.curSk = nil;
        g_Data.operable = true;
    end
    
    local follow = (skill.follow or speedEffect<=0);
    local parent = follow and g_Data.role or g_Data.scene.layer;
    if not follow then
        g_Data.curSk = nil;
        g_Data.operable = true;
    end
    
    local bulletNode = LoadArmature(effect, parent
        , follow and "rskill" or "rbullet", nil, follow and SkillEnd,skill);
    bulletNode.removeData = {follow and SkillEnd or RemoveCallback,skill};
    bulletNode.level = g_Data.role.level;
    bulletNode.pa = g_Data.role.pa;
    if g_Data.skNor == skill then
        bulletNode.ma = g_Data.role.ma;
    end
    bulletNode:setLocalZOrder(skill.isCom and 3 or 2)
    local s = GetNodeSize(bulletNode);
    if not follow then
        bulletNode:setPosition(cc.p(x+xShift,y+yShift))
    else
        local rs2 = g_Data.role:getContentSize();
        local ra = g_Data.role:getAnchorPoint();
        
        xShift = rs2.width * ra.x + xShift;
        yShift = -rs2.height * ra.y + yShift;

        bulletNode:setPosition(cc.p(xShift,yShift))
    end
    
    if speedEffect > 0 then
        if distEffect > g_Data.scene:getContentSize().width then
            distEffect = g_Data.scene:getContentSize().width;
        end
        MoveTo(bulletNode, speedEffect, distEffect, nil, follow and SkillEnd or RemoveCallback)
    end
    
    return true;
end

local function PetAttack()
    local pet = EXCEL.pet[g_Player.petOn];
    if pet then
        local now = os.clock();
        local skTm = g_Data.skcds[tostring(g_Data.pet)];
        if skTm and (now-skTm)*1000<1000 then
            return false;
        end
        g_Data.skcds[tostring(g_Data.pet)] = now;
        
        local bulletNode = LoadArmature(pet.petcom, g_Data.scene.layer
            , "rbullet", nil, RemoveCallback,skill);
        bulletNode.removeData = {RemoveCallback};
        bulletNode.level = g_Data.role.level;
        bulletNode.pa = g_Data.role.pa;
        bulletNode:setLocalZOrder(3);
        local s = GetNodeSize(bulletNode);
        local x,y = g_Data.role:getPosition();
        x = x + EXCEL.const.petShift[1];
        y = y + EXCEL.const.petShift[2];
        bulletNode:setPosition(cc.p(x+pet.petcom[3],y+pet.petcom[4]))
        x,y = bulletNode:getPosition();
        print(x,y)
        
        local dist = g_Data.scene:getContentSize().width;
        MoveTo(bulletNode, pet.petcom[5], dist, nil, RemoveCallback)
    end
end

local function onTouchBegan(touch, event)
    local location = touch:getLocation()
    
    return true;
end

local function onTouchEnded(touch, event)
    local location = touch:getLocation()
    
    return true;
end

local function AllItemJumpToRole(rx, ry)
    local childs = g_Data.scene.layer:getChildren();
    for i=1, #childs do
        local nodeA = childs[i];
        if nodeA and nodeA:getName()=="item" then  
            JumpToRole(nodeA, 10, rx,ry);
        end
    end
end

local function MoveRoleTo(xMul, yMul, useDist, func, speedMul)
    local role = g_Data.role;
    local x,y = role:getPosition();
    local s = GetNodeSize(g_Data.role);
    local w = s.width;
    local h = s.height;

    local maxX = g_Data.scene.width-w/2;
    local minX = w/2;
    local maxY = g_Data.scene.height-h/2;
    local minY = h/2;

    local sk = g_Data.curSk;
    local speed = sk and sk.speed or 0;
    local speed = speed + g_Data.gate.moveSpeed[1];
    
    speed = speed * (speedMul or 1)
    x = x + (useDist and 1 or speed) * xMul;    
    y = y + (useDist and 1 or speed) * yMul;

    if x<minX then
        x = minX;
    end
    if x>maxX then
        x = maxX;
    end
    if y<minY then
        y = minY;
    end
    if y>maxY then
        y = maxY;
    end
    
    local t = MoveTo(role, speed, x, y, func);
    AllItemJumpToRole(x,y);
end

local function onKeyPressed(keyCode, event)
    if g_Data.boss then
        return false;
    end
    
    if not g_Data.isRunning then
        return;
    end
    
    if not g_Data.operable then
        return;
    end
        
    if keyCode == cc.KeyCode.KEY_A then
        MoveRoleTo(-1, 0);
    elseif keyCode == cc.KeyCode.KEY_D then
        MoveRoleTo(1, 0);
    elseif keyCode == cc.KeyCode.KEY_W then
        MoveRoleTo(0, 1);
    elseif keyCode == cc.KeyCode.KEY_S then
        MoveRoleTo(0, -1);
    else
        if not g_Data.skillAble then
            return false;
        end
        
        for k,v in pairs(EXCEL.hotkey) do
            if keyCode == cc.KeyCode[k] then
                local skId = g_Data.roleExcelData[v.skill];
                local skill = skId and EXCEL.skill[skId];
                if skill then
                    g_Data.nextSk = skill;
                    
                    if g_Data.curSk == g_Data.skNor then
                        ReleaseSkill(g_Data.nextSk);
                    end
                end
            end
        end
    end
end

local function onKeyReleased(keyCode, event)
    if g_Data.boss then
        return false;
    end
    
    if keyCode == cc.KeyCode.KEY_A
        or keyCode == cc.KeyCode.KEY_D
        or keyCode == cc.KeyCode.KEY_S
        or keyCode == cc.KeyCode.KEY_W
    then
        g_Data.role:stopActionByTag(GAME_TAG.ACTION_MOVE_TAG);
    end
end

local function LoadGate(scene)
    local gate = g_Data.gate;

    for i=#g_Data.gate.layer,1,-1 do
        for j=2,1,-1 do
            local idx = 2 * i + 1 - j;
            local node = AddNode(gate.layer[i][2], "gate"..idx, scene);
            if node then
                node:setTag(i);
                local x = node:getContentSize().width*(2-j);
                g_Data.mapXy[idx] = x;
                node:setPosition(cc.p(x, scene.height/2))
            end
        end
    end

    local gateMon = EXCEL[gate.gateMon];
    local monster = {};
    for k,v in ipairs(gateMon) do
        local m = ShallowCopyTable(v);
        if k==1 then
            g_Data.firstMonster = m;
        end
        monster[#monster+1] = m;
    end
    table.sort(monster,function(a,b)return a.at>b.at end)
    g_Data.monsterQueue = monster;
end

local function countDamage(attacker, defender)
    local damage = 0;
    local die = false;
    
    local a = 0;
    local d = 0;
    if attacker.ma and attacker.ma>0 then 
        a = attacker.ma + attacker.pa;
        d = defender.md;
    else
        a = attacker.pa;
        d = defender.pd;
    end
    local diff = (1-(math.abs(defender.level-attacker.level)-2)/100);
    
    if attacker.level < defender.level -2 then
        damage = diff*a - d;
    elseif attacker.level > defender.level + 2 then
        damage = a - d * diff;
    else
        damage = a - d;
    end
    
    damage = math.ceil(damage);
    if damage <= CONST_FLOAT_DIFF then
        damage = 1;
    end
    if defender.chp < damage then
        damage = defender.chp;
    end
    
    --damage = defender.hp * 0.2;
    
    defender.chp = defender.chp - damage;
    if defender.chp <= CONST_FLOAT_DIFF then
        defender.chp = 0;
        die = true;
    end
    return damage, die;
end

local function Hurt(attacker, defender)
    local damage, die = countDamage(attacker, defender)
    
    --local label = cc.Label:createWithTTF(string.format("-%s", damage)
    --    ,EXCEL.const["damageFontFile"]
    --    ,EXCEL.const["damageSize"])
    --local color = EXCEL.const["damageColor"];
    --label:setColor(cc.c3b(color[1], color[2], color[3]))
    
    local label = nil;
    label = cc.Label:createWithBMFont(EXCEL.const.damageFontFile
         , string.format("-%s", damage))
    
    local x1,y1 = defender:getPosition();
    local s1 = GetNodeSize(defender);
    local w1, h1 = s1.width, s1.height;
    
    label:setPosition(cc.p(x1, y1+h1/2+label:getContentSize().height));
    g_Data.scene.layer:addChild(label)
    
    if true1 then
        MoveTo(label, 20 or EXCEL.const["damageSpeed"]
            , nil, y1 + EXCEL.const["damageDist"], RemoveCallback); 
    else
        local x,y = label:getPosition();
        local bezier = {
            cc.p(0, 300 / 2),
            cc.p(300, - 300 / 2),
            cc.p(300,100),
        }
        local bezierForward = cc.BezierBy:create(2, bezier)
        local action = cc.Sequence:create(bezierForward, cc.CallFunc:create(RemoveCallback))
        label:runAction(action);
    end
        
    if defender == g_Data.role then
        UpdatePlayerBlood();
    else
        local m = EXCEL.monster[defender.mid];
        if m and m.icon and m.icon~="" then
            g_Data.scene.monsterIcon:setVisible(true);
            g_Data.scene.monsterIconPng:setVisible(true);
            g_Data.scene.monsterIcon:loadTexture(m.icon)
        end
        if defender.tp ~= 3 then
            g_Data.scene.monsterBlood:setVisible(true);
            g_Data.scene.monsterBloodPng:setVisible(true);
            local percent = 100 * defender.chp / defender.hp;
            g_Data.scene.monsterBlood:setPercent(percent)
            g_Data.scene.monsterHp:setVisible(true);
            g_Data.scene.monsterHp:setString(string.format("%s/%s"
                    , defender.chp, defender.hp))
            
            local function hideMonsterBloodIcon()
                if os.clock() >= g_Data.showMonsterBloodIcon + 1 then
                    g_Data.scene.monsterIcon:setVisible(false);
                    g_Data.scene.monsterIconPng:setVisible(false);
                    g_Data.scene.monsterBlood:setVisible(false);
                    g_Data.scene.monsterBloodPng:setVisible(false);
                    g_Data.scene.monsterHp:setVisible(false);
                end
            end
            g_Data.showMonsterBloodIcon = os.clock();
            local action = cc.DelayTime:create(1)
            action = cc.Sequence:create(action,cc.CallFunc:create(hideMonsterBloodIcon))
            g_Data.scene.monsterBlood:runAction(action);
        else
            g_Data.scene.monsterBlood:setVisible(false);
            g_Data.scene.monsterBloodPng:setVisible(false);
            g_Data.scene.monsterHp:setVisible(false);
            
            local percent = 100 * defender.chp / defender.hp;
            g_Data.scene.bossBlood:setPercent(percent)
            g_Data.scene.bossHp:setVisible(true);
            g_Data.scene.bossHp:setString(string.format("%s/%s"
                , defender.chp, defender.hp))
        end
    end
    
    --怪物是否死亡
    if die then
        if defender:getName() == "monster" then
            MonsterDie(defender);
        --GateVictory();
        end
    end
end

function EnterGate(gateId)
    local fps = 0;
    local flushCDTime = 0;
    
    local function flushCdSk(now)
        if now - flushCDTime < 0.2 then
            return;
        end
        flushCDTime = now;

        for k,v in pairs(g_Data.roleExcelData) do
            local name = k;
            local skId = v;
            local skill = EXCEL.skill[skId];
            if skill then
                local skNode = g_Data.ui:getChildByName(name);
                local cdNode = g_Data.ui:getChildByName("cd"..name);
                if skNode and cdNode then
                    local cd = g_Data.skcds[tostring(g_Data.role)..skId];
                    local diff = skill.cd/1000 - (now - (cd or 0));
                    local isInCd = diff>=CONST_FLOAT_DIFF;
                    if skill.id==1003 then
                    --UtilDebugLog("now=%s, cd=%s, skill.cd=%s, skill.id=%s,diff=%s, isInCd=%s"
                    --        , now, cd, skill.cd, skill.id,diff, isInCd);
                    end
                    if not cd then
                        isInCd = false;
                    end
                    skNode:setEnabled(not isInCd);
                    
                    cdNode:setVisible(isInCd);
                    if isInCd then
                        local color = EXCEL.const["cdColor"];
                        cdNode:setColor(cc.c3b(color[1], color[2], color[3]))
                        cdNode:setFontSize(EXCEL.const["cdSize"])
                        cdNode:setString(string.format("%.0f", diff));
                    end
                end
            end
        end
    end

    local function _onUpdate()
        fps = fps + 1;
        if fps % 1 ~=0 then
            return;
        end
        
        if not g_Data.isRunning then
            return;
        end
        
        local now = os.time();
        local ck = os.clock();
        
        --刷新技能cd时间
        flushCdSk(ck);

        --地图刷新
        for i=2*#g_Data.gate.layer,1,-1 do
            local node = g_Data.scene:getChildByName("gate"..i);
            if node then
                local speed = g_Data.gate.layer[node:getTag()][1]
                local nodeSize = node:getContentSize();
                local tmpX = g_Data.mapXy[i] - speed;
                if tmpX + nodeSize.width*3/2 <= g_Data.scene.width then
                    tmpX = tmpX + nodeSize.width*2;
                end
                g_Data.mapXy[i] = tmpX;
                node:setPosition(cc.p(tmpX, g_Data.scene.height/2));
            end
        end
       
        --怪物出现
        for i=#g_Data.monsterQueue,1,-1 do
            if g_Data.monsterQueue[i].at <= (ck-g_Data.startCk)*1000 then
                AddMonster(g_Data.monsterQueue[i]);
                g_Data.monsterQueue[i] = nil;
            else
                break;
            end
        end
        
        --玩家攻击
        if not g_Data.boss then
            ReleaseSkill(g_Data.nextSk or g_Data.skNor);
            PetAttack();
        end
        
        --怪物攻击
        local childs = g_Data.scene.layer:getChildren();
        for i=1, #childs do
            local nodeA = childs[i];
            if nodeA:getName()=="monster" then
                ReleaseMonsterSkill(nodeA);
            end
        end
        
        --物品掉落
        for i=1, #childs do
            local nodeA = childs[i];
            if nodeA:getName()=="item" and 1==nodeA:getTag() then
                if checkIsBump(nodeA, g_Data.role, EXCEL.const["bumpPlayerCv"]) then
                    AddSitemData(nodeA:getTag(), 1);
                    RemoveCallback(nodeA);
                end
            end
        end
        
        if not g_Data.attackAble or g_Data.gameover then
            return;
        end
        
        --玩家子弹攻击怪物
        local hits = {};
        local childs = g_Data.scene.layer:getChildren();
        for i=1, #childs do
            local nodeA = childs[i];
            if nodeA:getName()=="rbullet" then
                for j=1,#childs do
                    local nodeB = childs[j];
                    if nodeB:getName()=="monster" and not hits[nodeB] then
                        if nodeB.tp==3 and g_Data.boss then

                        else
                            if checkIsBump(nodeA, nodeB, EXCEL.const["bumpBulletCv"]) then
                                hits[nodeB] = nodeA;
                                break;
                            end
                        end
                    end
                end
            end
        end
        for k,v in pairs(hits) do
            Hurt(v, k);
            v.removeData[1](v, v.removeData[2])
        end
        
        --怪物子弹攻击玩家
        local childs = g_Data.scene.layer:getChildren();
        for i=1, #childs do
            local nodeA = childs[i];
            local name = nodeA:getName();
            if name == "mbullet" or name=="mskill" then
                if checkIsBump(nodeA,g_Data.role, EXCEL.const["bumpBulletCv"]) then
                    if name=="mbullet" then
                        Hurt(nodeA, g_Data.role);
                        RemoveCallback(nodeA)
                    else
                        if CheckCrashCk(nodeA, ck, EXCEL.const["crashInterval"]) then
                            Hurt(nodeA, g_Data.role);
                            g_Data.crashCks[nodeA] = (ck-g_Data.startCk)*1000;
                        end
                    end
                    if g_Data.role.chp <= 0 then
                        GateDefeat();
                        return;
                    end
                end
            end
        end
        
        --怪物技能特效与子弹互消
        local childs = g_Data.scene.layer:getChildren();
        local hits = {};
        for i=1, #childs do
            for j=1,#childs do
                local nodeA = childs[i];
                local nameA = nodeA:getName();
                local nodeB = childs[j];
                local nameB = nodeB and nodeB:getName();
                if nameA=="mskill" and nameB=="rbullet"  then
                    if g_Data.boss then
                    
                    else
                        if checkIsBump(nodeA, nodeB, EXCEL.const["bumpBulletCv"]) then
                            hits[nodeB] = true;
                        end
                    end     
                end
            end
        end
        for k,v in pairs(hits) do
            RemoveCallback(k);
        end
        
        --玩家技能攻击怪物
        local hits = {};
        local childs = g_Data.role:getChildren();
        local childs2 = g_Data.scene.layer:getChildren();
        for i=1, #childs do
            local nodeA = childs[i];
            if nodeA:getName()=="rskill" then
                for j=1,#childs2 do
                    local nodeB = childs2[j];
                    if nodeB:getName()=="monster" and not hits[nodeB] then
                        if nodeB.tp==3 and g_Data.boss then

                        else
                            if checkIsBump(nodeA, nodeB, EXCEL.const["bumpBulletCv"]) then
                                hits[nodeB] = nodeA;
                            end
                        end     
                    end
                end
            end
        end
        for k,v in pairs(hits) do
            Hurt(v, k);
        end
        
        --玩家与怪物的互相碰撞攻击
        local childs = g_Data.scene.layer:getChildren();
        for i=1, #childs do
            local nodeA = childs[i];
            if nodeA and nodeA:getName()=="monster" then
                if nodeA.tp==3 and g_Data.boss then
                    
                else
                    if CheckCrashCk(nodeA, ck, EXCEL.const["crashInterval"]) then
                        if checkIsBump(nodeA, g_Data.role, EXCEL.const["bumpPlayerCv"]) then
                            g_Data.crashCks[nodeA] = (ck-g_Data.startCk)*1000;
                            Hurt(nodeA, g_Data.role);
                            if g_Data.role.chp <= 0 then
                                GateDefeat();
                                return;
                            end
                        end
                    end
                end
            end
        end
    end
    
    for k,v in pairs(g_DataInit) do
        g_Data[k] = v;
        if type(v) == "table" then
            g_Data[k] = {};
        end
    end
    
    --新手关卡
    local gate = EXCEL.gate[gateId];
    if not gate then
        return false;
    end
    g_Data.gate = gate;
    g_Data.roleName = "man";
    g_Data.roleExcelData = EXCEL.roles[g_Data.roleName];
    g_Data.skNor = EXCEL.skill[g_Data.roleExcelData.skNor];
    local sk = EXCEL.skill[gate.com];
    if sk then
        g_Data.skNor = EXCEL.skill[gate.com];
    end
    g_Data.nextSk = g_Data.skNor;
    if gateId == EXCEL.const.newbieGate then
        g_Data.skillAble = false;
    end
    
    local old = os.clock();
    local scene = LoadJson("fight.json", _onUpdate);
    g_Data.scene = scene;
    g_Data.gateId = gateId;
    g_Data.ui = scene.layer:getChildByName("ui")

    g_Data.ui:setLocalZOrder(10);
    
    LoadAllNode(scene, g_Data.ui);
    
    scene.width = scene:getContentSize().width;
    scene.height = scene:getContentSize().height;
    
    --事件响应
    local listener = cc.EventListenerTouchOneByOne:create()
    listener:registerScriptHandler(onTouchBegan,cc.Handler.EVENT_TOUCH_BEGAN)
    listener:registerScriptHandler(onTouchEnded,cc.Handler.EVENT_TOUCH_ENDED)
    --scene:getEventDispatcher():addEventListenerWithSceneGraphPriority(listener, scene)
    
    local listener = cc.EventListenerKeyboard:create()
    listener:registerScriptHandler(onKeyPressed, cc.Handler.EVENT_KEYBOARD_PRESSED)
    listener:registerScriptHandler(onKeyReleased, cc.Handler.EVENT_KEYBOARD_RELEASED)
    scene:getEventDispatcher():addEventListenerWithSceneGraphPriority(listener, scene)
    
    --加载关卡
    LoadGate(scene);

    --加载主角
    local nextRolePlay = nil;
    nextRolePlay = function (node, args)
        local nextArmature = g_Data.nextArmature or g_Data.roleExcelData.await;
        g_Data.nextArmature = nil;
        PlayArmature(g_Data.role, nextArmature, nextRolePlay)
    end
    
    g_Data.role = LoadArmature(g_Data.roleExcelData.await, g_Data.scene.layer, nil, nil, nextRolePlay);
    local s = GetNodeSize(g_Data.role);
    g_Data.role:setPosition(cc.p(s.width/2,scene:getContentSize().height/2));
    
    g_Data.role.level = g_Player.level;
    local lv = EXCEL.level[g_Data.role.level];
    g_Data.role.hp = lv.hp;
    g_Data.role.pa = lv.pa;
    g_Data.role.pd = lv.pd;
    g_Data.role.ma = lv.ma;
    g_Data.role.md = lv.md;
    local pet = EXCEL.pet[g_Player.petOn];
    if pet then
        for k,v in pairs(pet.attr) do
            local name = CONST.attrs[v[1]];
            g_Data.role[name] = g_Data.role[name] + v[2]; 
        end
    end
    for k,v in pairs(EXCEL.equip) do
        local curLv = g_Player.equips[k];
        local intensifyId = v.intensify[1] + curLv-1;
        local intensify = EXCEL.intensify[intensifyId];
        if intensify then
            for k,v in pairs(intensify.attr) do
                local name = CONST.attrs[v[1]];
                g_Data.role[name] = g_Data.role[name] + v[2]; 
            end
        end
    end  
    g_Data.role.chp = g_Data.role.hp;
    
    local x,y = g_Data.role:getPosition();
    local w,h = s.width,s.height;
    UpdatePlayerBlood();
    
    local petExl = EXCEL.pet[g_Player.petOn];
    g_Data.pet = nil;
    if petExl then
        g_Data.pet = LoadArmature(petExl.fightAnimation, g_Data.role);
        
        local rs2 = g_Data.role:getContentSize();
        local ra = g_Data.role:getAnchorPoint();
        g_Data.pet:setPosition(cc.p(rs2.width * ra.x+EXCEL.const.petShift[1]
            , -rs2.height * ra.y+EXCEL.const.petShift[2]))
    end

    --cocostudio中的参照背景隐藏
    
    --刚开始时，隐藏小怪头像和血条
    g_Data.scene.monsterIcon:setVisible(false);
    g_Data.scene.monsterIconPng:setVisible(false);
    g_Data.scene.monsterBlood:setVisible(false);
    g_Data.scene.monsterBloodPng:setVisible(false);
    g_Data.scene.bossBlood:setVisible(false);
    g_Data.scene.bossBloodPng:setVisible(false);
    g_Data.scene.monsterHp:setVisible(false);
    g_Data.scene.bossHp:setVisible(false);
    
    --加载关卡名称
    local name = g_Data.ui:getChildByName("gateName")
    if name then
        name:loadTexture(gate.namePngPath)
    end

    --设置菜单
    local function exitGate(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            EnterSetting();
        end
    end
    local exitGateButton = g_Data.ui:getChildByName("exitGate")
    exitGateButton:addTouchEventListener(exitGate)
    
    local move = function(x, y)
        if not g_Data.isInMove then
            g_Data.isInMove = true;
            local mx = (x - g_Data.move0X);
            local my = (y - g_Data.move0Y);
            local function afterMove()
                g_Data.isInMove = false;
            end
            MoveRoleTo(mx, my, true, afterMove, 2);
        end
    end
    
    --方向键
    local function moveFunc(sender, eventType)
        local b = sender:getTouchBeganPosition();
        local p = sender:getTouchMovePosition()
        g_Data.moveTp = eventType;
      
        if eventType == ccui.TouchEventType.began then
            move(b.x, b.y)
            g_Data.moveX, g_Data.moveY = p.x, p.y;

        elseif eventType == ccui.TouchEventType.moved then
            local afterMove = nil;

            afterMove = function(node)
                if g_Data.moveTp == ccui.TouchEventType.moved
                    or g_Data.moveTp == ccui.TouchEventType.began then
                    move(g_Data.moveX, g_Data.moveY);
                end
            end
            
            move(p.x, p.y)
            sender:setPosition(cc.p(p.x, p.y))
            g_Data.moveX, g_Data.moveY = p.x, p.y;
        else
            sender:setPosition(cc.p(g_Data.move0X, g_Data.move0Y))
        end
    end
    local moveButton = g_Data.ui:getChildByName("move")
    moveButton:addTouchEventListener(moveFunc)
    g_Data.move0X, g_Data.move0Y = moveButton:getPosition();
    
    local function clickSk(sender, eventType)
        if eventType == ccui.TouchEventType.ended then
            local skId = g_Data.roleExcelData[sender:getName()];
            local skill = skId and EXCEL.skill[skId];
            if skill then
                g_Data.nextSk = skill;
            end
        end
    end
    local childs = g_Data.ui:getChildren();
    for i=1, #childs do
        local name = childs[i]:getName();
        local skId = g_Data.roleExcelData[name];
        if skId and EXCEL.skill[skId] then
            SetButtonFunc(childs[i], clickSk, "sk"..i)
            
            local cdNode = g_Data.ui:getChildByName("cd"..name);
            if cdNode then
                cdNode:setVisible(false);
            end
        end
    end
    --进入场景
    
    PushScene(scene);
    
    AudioEngine.playMusic(EXCEL.sound.gate.value, true)
    
    if EXCEL.gate[testGateId] then
        g_Data.isRunning = true;
        g_Data.operable = true;
        g_Data.startCk = os.clock();
        scene.layer:setEnabled(true)
        if 1 then return true end;
    end
    
    local function postFunc0()  
        g_Data.isRunning = true;
        g_Data.operable = true;
        g_Data.startCk = os.clock();
        scene.layer:setEnabled(true)
    end
    local function postFunc()
        local arr = nil;
        local shade = AddShade(g_Data.scene.layer);
        local function postFunc2()
            postFunc0();
            g_Data.scene.layer:removeChild(shade);
            g_Data.scene.layer:removeChild(arr);
        end
        arr = LoadArmature(EXCEL.const["daoshuAnimation"], g_Data.scene.layer, nil, nil, postFunc2);
        arr:setPosition(cc.p(g_Data.scene.width/2, g_Data.scene.height/2));
    end
    scene.layer:setEnabled(false);
    if gateId == EXCEL.const.newbieGate then
        postFunc0();
    else
        EnterTalk(g_Data.ui, gate.talk[1], postFunc);
    end
    
    return true;
end




